#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <inttypes.h>
#include <unistd.h>
#include <sys/time.h>
#include <scene.h>
#include "config.h"
#include "opt.h"
#include "fb.h"
#include "progress.h"
#include "tpool.h"
#include "rt.h"

int init(void);
void cleanup(void);
void render(void);
void render_block(struct block *blk);
void output(void);
static void block_done(struct block *blk);
static void sighandler(int sig);

struct scene *scn;

int main(int argc, char **argv)
{
	struct timeval tv, tv0;
	unsigned int min, sec, msec;

	if(parse_opt(argc, argv) == -1) {
		return 1;
	}

	if(init() == -1) {
		return 1;
	}

	gettimeofday(&tv0, 0);
	render();
	gettimeofday(&tv, 0);

	msec = (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;
	sec = msec / 1000;
	msec -= sec * 1000;
	min = sec / 60;
	sec -= min * 60;
	fprintf(stderr, "rendering time: %u min, %u sec, %u msec\n", min, sec, msec);

	output();
	return 0;
}

int init(void)
{
	if(!(fb = alloc_framebuffer())) {
		return -1;
	}

	setenv("RENDY_PLUGIN_PATH", RENDY_PLUGIN_PATH, 1);

	if(!(scn = scn_create())) {
		fprintf(stderr, "failed to create scene\n");
		cleanup();
		return -1;
	}

	if(scn_read_file(scn, stdin) == -1) {
		fprintf(stderr, "error while reading scene data\n");
		cleanup();
		return -1;
	}

	if(init_threads(opt.threads) == -1) {
		fprintf(stderr, "thread pool init failed\n");
		cleanup();
		return -1;
	}

	progr_init();

	signal(SIGTERM, sighandler);
	signal(SIGSEGV, sighandler);
	signal(SIGINT, sighandler);

	atexit(cleanup);
	return 0;
}

void cleanup(void)
{
	progr_cleanup();
	if(scn) {
		scn_free(scn);
	}
	if(fb) {
		free_framebuffer(fb);
	}
}

void render(void)
{
	set_block_render_func(render_block);
	set_block_done_func(block_done);

	start_frame(opt.blk_sz, BLK_PRI_GAUSSIAN);

	wait_frame();
}

void render_block(struct block *blk)
{
	int x, y;
	struct pixel *pix;

	pix = blk->bptr;
	for(y=0; y<blk->ysz; y++) {
		for(x=0; x<blk->xsz; x++) {
			struct subpixel subpix;
			ray_t ray = primary_ray(x + blk->x, y + blk->y, 0);

			trace(scn, ray, &subpix);

			pix[x].r = subpix.r;
			pix[x].g = subpix.g;
			pix[x].b = subpix.b;
		}
		pix += fb->width;
	}
}

void output(void)
{
	static const uint16_t bom = 0xaabb;
	int i, j;
	struct pixel *pix = fb->pix;

	fwrite(&bom, sizeof bom, 1, stdout);
	fwrite(&opt.xmin, sizeof opt.xmin, 1, stdout);
	fwrite(&opt.ymin, sizeof opt.ymin, 1, stdout);
	fwrite(&opt.xsz, sizeof opt.xsz, 1, stdout);
	fwrite(&opt.ysz, sizeof opt.ysz, 1, stdout);

	for(i=opt.ymin; i<opt.ysz; i++) {
		for(j=opt.xmin; j<opt.xsz; j++) {
			fwrite(&pix->r, sizeof pix->r, 1, stdout);
			fwrite(&pix->g, sizeof pix->g, 1, stdout);
			fwrite(&pix->b, sizeof pix->b, 1, stdout);
			pix++;
		}
	}
	fflush(stdout);
}

static void block_done(struct block *blk)
{
	int done, max, p;
	
	p = get_work_status(&done, &max);
	progress(done, done, max);
}

static void sighandler(int sig)
{
	psignal(sig, "caught signal");
	exit(0);
}
